﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Aliphant.ChatAI.Interfaces;
using Aliphant.ChatAI.Models;
using Aliphant.ChatAI.Utility;
using Aliphant.Common;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace Aliphant.ChatAI.Proxys
{
    public class GptAgent : IChatAI
    {
        protected GptConfig agentConfig;

        protected readonly string currentChat = "chat.json";
        protected readonly string historyChat = "history.json";

        protected readonly Queue<string> _messageQueue;
        protected readonly ManualResetEvent _pauseEvent;

        protected bool _isRun = false;
        protected HttpClient client;
        protected Thread _workerThread;

        protected JArray messages;  // 对话内容（不包含深度思考）
        protected JArray historyMsg;    // 历史对话（包含深度思考）
        protected JObject requestBody;  // 请求体

        private CancellationTokenSource? _cts;

        public GptAgent()
        {
            this.agentConfig = new GptConfig(string.Empty);
            // 初始化
            client = new HttpClient();
            _messageQueue = new Queue<string>();
            _pauseEvent = new ManualResetEvent(false);
            messages = new JArray();
            historyMsg = new JArray();
            requestBody = new JObject();
            _workerThread = new Thread(Run);
        }


        #region 接口

        public event Action<ChatContent>? OutPutHandle;
        public event Action? InPutHandle;

        public void Start()
        {
            _isRun = true;
            _workerThread.Start();
        }

        public void Load(GptConfig config)
        {
            Pause();

            this.agentConfig = config;
            // 初始化Http和请求体
            client = CreateClient();
            requestBody = CreateRequest();
        }

        public void Stop()
        {
            if (_isRun)
            {
                _isRun = false;
                _cts?.Cancel();
                _pauseEvent.Set();
                _workerThread.Join();
            }
        }

        public void Pause()
        {
            _cts?.Cancel();
            _messageQueue.Clear();
        }

        public void WeekUp()
        {
            _pauseEvent.Set();
        }

        public void AddMessage(string message)
        {
            _messageQueue.Enqueue(message);
        }

        public void NewSession()
        {
            // 暂停当前输出
            Pause();

            // 初始化消息缓存
            messages = CreateNewChat();
            historyMsg = CreateNewChat();
            // 更新消息缓存文件
            SaveChatLog(messages, currentChat);
            SaveChatLog(historyMsg, historyChat);
            // 打个招呼
            SpeekHello(false);
        }

        public void RefreshSession()
        {
            // 暂停当前输出
            Pause();
            // 加载消息缓存
            messages = LoadChatList(currentChat);
            historyMsg = LoadChatList(historyChat);
            // 打印历史对话
            PrintHistoryChat();
        }

        #endregion

        protected async void Run()
        {
            while (_isRun)
            {
                _pauseEvent.WaitOne();  // 等待唤醒
                if (!_isRun) break;

                try
                {
                    if (_messageQueue.Count == 0) continue;

                    var question = _messageQueue.Dequeue();
                    var content = new ChatContent("user", question);

                    _cts = new CancellationTokenSource();
                    await Send(content, _cts.Token);

                    InPutHandle?.Invoke();
                }
                catch (Exception ex)
                {
                    Logger.Error(ex);
                }

                _pauseEvent.Reset();    // 重置信号
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="content">发送消息</param>
        /// <param name="token"></param>
        /// <returns></returns>
        protected async Task Send(ChatContent content, CancellationToken token)
        {
            if (string.IsNullOrEmpty(content.Role)) return;

            messages.Add(CreateMessage(content.Message, content.Role));
            historyMsg.Add(CreateMessage(content.Message, content.Role));

            requestBody["messages"] = messages;
            string msgStr = JsonConvert.SerializeObject(requestBody).ToString();
            StringContent strCnt = new StringContent(msgStr, null, "application/json");

            try
            {
                var httpreq = new HttpRequestMessage(HttpMethod.Post, agentConfig.API) { Content = strCnt };
                var response = await client.SendAsync(httpreq, HttpCompletionOption.ResponseHeadersRead);
                response.EnsureSuccessStatusCode();

                if (response.IsSuccessStatusCode)
                {
                    string? line;
                    StringBuilder? assistant = null;
                    StringBuilder? reasoning = null;

                    var responseStream = await response.Content.ReadAsStreamAsync();
                    using var reader = new StreamReader(responseStream);

                    while (!reader.EndOfStream && !token.IsCancellationRequested)
                    {
                        line = await reader.ReadLineAsync();
                        dynamic? json = AnalysisResponse(line);
                        if (json == null)
                        {
                            continue;
                        }

                        if (json.choices[0].delta.role != null)
                        {
                            assistant = new StringBuilder();
                        }
                        if (json.choices[0].delta.reasoning_content != null && reasoning == null)
                        {
                            reasoning = new StringBuilder();
                        }

                        //获取并输出内容
                        string cnt = json.choices[0].delta.content;
                        string rcnt = json.choices[0].delta.reasoning_content;
                        assistant?.Append(cnt);
                        reasoning?.Append(rcnt);
                        SendAnswer(cnt, rcnt);
                    }

                    OutPutHandle?.Invoke(ChatContent.END);   // 结束

                    // 暂停不写入文件
                    if (token.IsCancellationRequested)
                    {
                        messages.RemoveAt(messages.Count - 1);
                        historyMsg.RemoveAt(historyMsg.Count - 1);
                        return;
                    }

                    // 更新对话
                    if (reasoning != null)
                    {
                        historyMsg.Add(CreateMessage(reasoning.ToString().Trim('\r', '\n'), "reasoning"));
                    }
                    if (assistant != null)
                    {
                        messages.Add(CreateMessage(assistant.ToString().Trim('\r', '\n'), "assistant"));
                        historyMsg.Add(CreateMessage(assistant.ToString().Trim('\r', '\n'), "assistant"));
                    }

                    SaveChatLog(messages, currentChat);
                    SaveChatLog(historyMsg, historyChat);
                }
            }
            catch (Exception ex)
            {
                Logger.Error($"URL: {agentConfig.API}, Content: {msgStr}, EX: {ex}");
            }
        }

        protected JObject? AnalysisResponse(string? response)
        {
            Logger.Info($"GptAgent.AnalysisResponse - {response}");

            if (string.IsNullOrEmpty(response) ||
                response.Contains("[DONE]") ||
                response.Contains(": keep-alive"))
            {
                return null;
            }

            response = response.Substring(6);
            try
            {
                return JObject.Parse(response);
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// 发送回答
        /// </summary>
        /// <param name="cnt">回答内容</param>
        /// <param name="rcnt">思考内容</param>
        protected void SendAnswer(string cnt, string rcnt)
        {
            //if (requestBody["model"]?.ToString() == "deepseek-reasoner")
            //{
            //    OutputContent("reasoning", rcnt);
            //}
            OutputContent("reasoning", rcnt);
            OutputContent("assistant", cnt);
        }

        protected void OutputContent(string role, string message)
        {
            OutPutHandle?.Invoke(new ChatContent(role, message));
        }

        #region 工具方法

        protected virtual HttpClient CreateClient()
        {
            var client = new HttpClient();
            client.DefaultRequestHeaders.Add("Accept", "application/json");
            client.DefaultRequestHeaders.Add("Authorization", "Bearer " + agentConfig.Key);
            return client;
        }

        private JObject CreateRequest()
        {
            string jsonStr = @"{
  'frequency_penalty': 0,
  'presence_penalty': 0,
  'stop': null,
  'stream_options': null,
  'top_p': 1,
  'tool_choice': 'none',
  'logprobs': false,
  'top_logprobs': null,
  'tools': null,
  'response_format': {
    'type': 'text'
  },
  'parameters': {
    'result_format': 'message'
  },
  'stream_options': {
    'include_usage': false
  }
}";
            var json = JObject.Parse(jsonStr);
            json["model"] = agentConfig.Model;
            json["max_tokens"] = agentConfig.MaxTokens;
            json["temperature"] = agentConfig.Temperature;
            json["stream"] = agentConfig.Stream;

            return json;
        }

        protected virtual JArray LoadChatList(string chatFile)
        {
            string fileName = Path.Combine(agentConfig.Path, chatFile);

            JArray? msgs = JsonHelper.ReadJsonArray(fileName);
            if (msgs != null)
            {
                return msgs;
            }

            msgs = new JArray();
            if (!string.IsNullOrEmpty(agentConfig.System))
            {
                msgs.Add(CreateMessage(agentConfig.System, "system"));
            }
            SaveChatLog(msgs, chatFile);
            return msgs;
        }

        protected static JObject CreateMessage(string content, string role)
        {
            return new JObject
            {
                ["content"] = content,
                ["role"] = role
            };
        }

        protected void SaveChatLog(JArray messages, string file)
        {
            string fileName = Path.Combine(agentConfig.Path, file);
            JsonHelper.SaveJArray(messages, fileName);
        }

        protected void SpeekHello(bool stream = false)
        {
            if (stream)
            {
                char[] tokens = agentConfig.Tips.ToCharArray();
                foreach (var token in tokens)
                {
                    OutPutHandle?.Invoke(new ChatContent("assistant", token.ToString()));
                    Thread.Sleep(35);
                }
            }
            else
            {
                OutPutHandle?.Invoke(new ChatContent("assistant", agentConfig.Tips));
            }

            historyMsg.Add(CreateMessage(agentConfig.Tips, "assistant"));
            SaveChatLog(historyMsg, historyChat);
            OutPutHandle?.Invoke(ChatContent.END);
        }

        protected void PrintHistoryChat()
        {
            foreach (JObject message in historyMsg)
            {
                if (!message.TryGetValue("role", out JToken? roleToken) || !message.TryGetValue("content", out JToken? contentToken))
                {
                    continue;
                }

                string role = roleToken?.Value<string>() ?? "";
                string content = contentToken?.Value<string>() ?? "";

                OutPutHandle?.Invoke(new ChatContent(role, content));
                OutPutHandle?.Invoke(ChatContent.END);
            }
            OutPutHandle?.Invoke(ChatContent.END);
        }

        protected JArray CreateNewChat()
        {
            JArray messages = [];
            if (!string.IsNullOrEmpty(agentConfig.System))
            {
                messages.Add(CreateMessage(agentConfig.System, "system"));
            }
            return messages;
        }

        #endregion
    }
}
